package com.ss.web.tagmark.filter;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.WriteListener;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Configuration;

/**
 * 通过Configuration自动增加到Filter体系中，实现基于cookie的提示信息的功能。
 * 
 * @author taofucheng
 *
 */
@Configuration
public class CookieBasedMessageFilter implements Filter {
	private static ThreadLocal<Map<String, String>> cookieMsgs = new ThreadLocal<>();

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		cookieMsgs.set(null);
		if (request instanceof HttpServletRequest) {
			request = new CookieBasedRequest((HttpServletRequest) request);
		}
		if (response instanceof HttpServletResponse) {
			response = new CookieBasedResponse((HttpServletResponse) response);
		}
		chain.doFilter(request, response);
	}

	@Override
	public void destroy() {
		cookieMsgs.set(null);
	}

	public static void setCookieValue(String key, String content) {
		Map<String, String> cms = cookieMsgs.get();
		if (cms == null) {
			cms = new LinkedHashMap<>();
			cookieMsgs.set(cms);
		}
		cms.put(key, content);
	}

	/**
	 * 将值写入cookie。
	 * 
	 * @param response
	 */
	private static void writeCookies(ServletResponse response) {
		try {
			Map<String, String> cms = cookieMsgs.get();
			if (cms != null && cms.size() > 0) {
				//将这些key与value写入cookie中
				if (response instanceof HttpServletResponse && (!response.isCommitted())) {
					for (String key : cms.keySet()) {
						String val = cms.get(key);
						Cookie c = new Cookie(key, val);
						c.setHttpOnly(true);
						c.setPath("/");
						c.setMaxAge(StringUtils.isEmpty(val) ? 0 : -1);// 浏览器关闭就删除
						((HttpServletResponse) response).addCookie(c);
					}
					cms.clear();//写一次清除掉
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 在forword之前写一下cookie
	 * 
	 * @author taofucheng
	 *
	 */
	private static class CookieBasedRequest extends HttpServletRequestWrapper {

		public CookieBasedRequest(HttpServletRequest request) {
			super(request);
		}

		public RequestDispatcher getRequestDispatcher(String path) {
			RequestDispatcher rd = super.getRequestDispatcher(path);
			if (rd != null) {
				rd = new RequestDispatcherWrapper(rd);
			}
			return rd;
		}
	}

	private static class RequestDispatcherWrapper implements RequestDispatcher {
		private RequestDispatcher rd;

		RequestDispatcherWrapper(RequestDispatcher rd) {
			this.rd = rd;
		}

		@Override
		public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException {
			writeCookies(response);
			rd.forward(request, response);
		}

		@Override
		public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException {
			rd.include(request, response);
			writeCookies(response);
		}

	}

	private static class CookieBasedResponse extends HttpServletResponseWrapper {

		public CookieBasedResponse(HttpServletResponse response) {
			super(response);
		}

		public void sendRedirect(String location) throws IOException {
			writeCookies(this);
			super.sendRedirect(location);
		}

		public void sendError(int sc, String msg) throws IOException {
			writeCookies(this);
			super.sendError(sc, msg);
		}

		@Override
		public void sendError(int sc) throws IOException {
			writeCookies(this);
			super.sendError(sc);
		}

		public void flushBuffer() throws IOException {
			writeCookies(this);
			super.flushBuffer();
		}

		public PrintWriter getWriter() throws IOException {
			return new PrintWriterWrapper(super.getWriter(), this);
		}

		public ServletOutputStream getOutputStream() throws IOException {
			return new ServletOutputStreamWrapper(super.getOutputStream(), this);
		}

	}

	private static class PrintWriterWrapper extends PrintWriter {
		private HttpServletResponse response;

		public PrintWriterWrapper(Writer out, HttpServletResponse response) {
			super(out);
			this.response = response;
		}

		public void close() {
			writeCookies(response);
			super.close();
		}
	}

	/**
	 * 主要是在close之前，写一下cookie
	 * 
	 * @author taofucheng
	 *
	 */
	private static class ServletOutputStreamWrapper extends ServletOutputStream {
		private ServletOutputStream sos = null;
		private HttpServletResponse response = null;

		public ServletOutputStreamWrapper(ServletOutputStream sos, HttpServletResponse response) {
			if (sos == null) {
				throw new IllegalArgumentException("ServletOutputStream cannot be null!");
			}
			this.sos = sos;
			this.response = response;
		}

		@Override
		public boolean isReady() {
			return sos.isReady();
		}

		@Override
		public void setWriteListener(WriteListener listener) {
			sos.setWriteListener(listener);
		}

		@Override
		public void write(int b) throws IOException {
			sos.write(b);
		}

		public void close() throws IOException {
			writeCookies(response);
			sos.close();
		}
	}
}
